// 一. 理解属性
// 1.创建对象
const obj = new Object();
obj.width = 20;
obj.height = 40
obj.getWidth = () => {
    // ...
}

const o = {
    width: 20,
    height: 40,
    getWidth: () => {
        // ...
    }
}

// configureable, enumerable, writeable, value

// 访问器：getter(get) setter(set)

// 2.属性
const data = {};
Object.defineProperties(data, {
    code: {
        value: 1,
    },
    message: {
        value: 'success'
    }
})

const code = Object.getOwnPropertyDescriptor(data, 'code');

// 3.合并
Object.assign();

// 4.相等判定
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN));

// TODO: Object.is() 如何判断超过2个值

// 5.扩展对象
const key = 'age';
data[key] = '20'

// 6.解构 - ToObject()
const { length } = 'alibaba'; // length -> 7
const { constructor: c } = 4; // c -> Number

// 二.创建对象
// 1.工厂模式 - 没有解决对象标识问题
function createPerson(name, age) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.getName = () => {
        console.log(this.name);
    }
    return o;
}
// 2.构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getName = () => {
        console.log(this.name);
    }
}

const p1 = new Person('a', 20);
const p2 = new Person('b', 23);

p1.constructor === Person;
p1 instanceof Person // true
p1 instanceof Object // true

p1.getName === p2.getName // false

function getName() {
    console.log(this.name);
}

let Person = function (name = 'a', age = 20) {
    this.name = name;
    this.age = age;
    // this.getName = () => {
    //     console.log(this.name);
    // }
    this.getName = getName;
}

// const p1 = new Person()
// const p2 = new Person;

const p1 = new Person('c', 24);
Person('d', 21); // 属性 挂载window
// window.getName()

// 3.原型模式
// function Studuent() {}
const Studuent = function() {};
// Studuent.prototype.constructor === Studuent;
// Studuent.prototype.name = 'a';
// Studuent.prototype.age = 18;
// Studuent.prototype.height = 170;
// Studuent.prototype.getName = () => {
//     console.log(this.name);
// }

Studuent.prototype = {
    name: 'a',
    age:18,
    height: 170,
    getName: () => {
        // ...
    }
}

Object.defineProperty(Studuent.prototype, 'constructor', {
    enumerable: false,
    value: Studuent
})

const s1 = new Studuent();
const s2 = new Studuent();

Object.getPrototypeOf(s1) === Studuent.prototype; // true
// Object.setPrototypeOf(); // 影响性能
Object.create();

s1.hasOwnProperty('name'); // false
'name' in s1 // true

// 迭代
// Object.values()  // Object.entries();

const s = Symbol();
const o = {
    [s]: 'a'
}
Object.values(o);


// 原型链
function Super() {
    this.superFlag = true;
}

Super.prototype.getSuperFlag = function() {
    return this.flag;
}

function Sub() {
    this.subFlag = false;
}

Sub.prototype = new Super();

// Super.prototype = {
//     // ..
// }
// 会导致上面继承失效

Sub.prototype.getSubFlag = function() {
    return this.subFlag;
}

const instnace = new Sub();
Sub.prototype.constructor // -> Super
instance.constructor // -> Super
instnace.getSuperFlag() // true;

instnace instanceof Super // true
instnace instanceof Sub  // true

// 盗用构造函数 - x 函数不能重用
function Super(name) {
    this.name = name;
    this.colors = ['red', 'yellow', 'green']
}

function Sub() {
    Super.call(this, 'name')
}

const s3 = new Sub();
s3.colors.push('black'); //'red', 'yellow', 'green', 'black'

const s4 = new Sub;
s4.colors // 'red', 'yellow', 'green'

// 组合继承
function Super(name) {
    this.name = name;
    this.colors = ['red', 'yellow', 'green']
}

Super.prototype.getName = () => {
    console.log(this.name);
}

function Sub(name, age) {
    Super.call(this, name); // 调用2
    this.age = age;
}

Sub.prototype = new Super(); // 调用1
Sub.prototype.getAge = () => {
    console.log(this.age);
}

const s5 = new Sub('e', 28);
s5.colors.push('black'); // 'red', 'yellow', 'green', 'black'
s5.getName() // e
s5.getAge() // 28

const s6 = new Sub('f', 29);
s6.getName() // f
s6.getAge() // 29

// 原型式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

Object.create();

const p = {
    name: 'g',
    colors: ['red', 'yellow', 'green']
}

const p3 = object(p);
p3.colors.push('black');

const p4 = object(p);
p4.colors.push('pink');

// 寄生式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function createO(original) {
    let clone = object(original)
    clone.getName1 = () => {
        console.log('a');
    }
    return clone;
}

// 寄生式组合继承 -> 引用类型继承最佳解决方式
function Super(name) {
    this.name = name;
    this.colors = ['red', 'yellow', 'green']
}

Super.prototype.getName = () => {
    console.log(this.name);
}

function Sub(name, age) {
    Super.call(this, name); // 调用2
    this.age = age;
}

function initProperty(subtype, supertype) {
    const prototype = object(supertype.prototype); // 创建
    prototype.constructor = subtype; // 增强
    subtype.prototype = prototype; // 赋值
}

initProperty(Sub, Super)
Sub.prototype.getAge = () => {
    console.log(this.age);
}

// es6 class
class Person {} // 类声明
const Person = class {}; // 类表达式
// 函数声明可以提升 类定义不可以提升
// 函数受函数作用域限制 类受块作用域限制

typeof Person // -> function

// 抽象基类 new.target
class Person {
    constructor() {
        if (new.target === 'xxx') {
            throw new Error('xxx cannot be init');
        }
    }
}
